﻿using System;
using System.Collections.Generic;
using System.Reflection;

namespace ArgusLight.Fractals2D
{
	public static class ColoringConverter
	{
		private static List<Type> converterClasses = new List<Type>(new Type[] { typeof(ColoringConverter.Converter) });

		//public delegate bool ConversionHandler(Coloring Source, ref Coloring Destination);
		
		/// <summary>
		/// Register a class which contains Converter-Methods.
		/// Convertermethods must be of the form
		/// <c>public static bool MethodName(ColoringSource Source, ref ColoringDest Destination);</c>
		/// ColoringSource and ColoringDest must be classes which inherit from <see cref="Coloring"/>.
		/// </summary>
		/// <param name="ConverterClass"></param>
		public static void RegisterConverter(Type ConverterClass)
		{
			if (converterClasses.Contains(ConverterClass) == false)
				converterClasses.Add(ConverterClass);
		}

		/// <summary>
		/// Tries to convert the source <see cref="Coloring"/> to the destination Coloring.
		/// </summary>
		/// <param name="Source">If the conversion was successful, this parameter is set to <see cref="null"/>.</param>
		/// <param name="Destination"></param>
		/// <returns>true if the conversion was successful, otherwise false.</returns>
		public static bool TryConversion(ref Coloring Source, ref Coloring Destination)
		{
			if (Source == null || Source.IsReady == false || Destination == null)
				return false;
			MethodInfo mi = FindConverter(Source.GetType(), Destination.GetType());
			if (mi == null)
				return false;
			object[] args = new object[] { Source, Destination };
			bool RetVal = (bool)mi.Invoke(null, args);
			if (RetVal == true)
				Source = null;
			return RetVal;
		}

		public static bool ConversionExists(Type Source, Type Destination)
		{
			MethodInfo mi = FindConverter(Source, Destination);
			return mi != null && mi.Name != "Standard";
		}

		//public static bool ConversionPossible(Type SourceType, Type DestinationType)
		//{
		//	if (FindConverter(SourceType, DestinationType) != new ConversionHandler(Converter.Standard))
		//		return true;
		//	return false;
		//}

		private static MethodInfo FindConverter(Type Source, Type Destination)
		{
			List<MethodInfo> mInfos = new List<MethodInfo>();
			foreach (Type type in converterClasses)
			{
				MethodInfo[] m = type.GetMethods(BindingFlags.Public | BindingFlags.Static);
				if (m != null)
					mInfos.AddRange(m);
			}
			List<MethodInfo> sel = new List<MethodInfo>();
			foreach (MethodInfo mi in mInfos)
			{
				ParameterInfo[] pi = mi.GetParameters();
				if (pi == null || pi.Length != 2)
					continue;
				if (Source != pi[0].ParameterType && Source.IsSubclassOf(pi[0].ParameterType) == false)
					continue;
				if (Destination != pi[1].ParameterType.GetElementType() && Destination.IsSubclassOf(pi[1].ParameterType.GetElementType()) == false)
					continue;
				sel.Add(mi);
			}

			if (mInfos.Count < 1)
				return null;

			mInfos = sel;

			sel = new List<MethodInfo>();
			for (int i = 0; i < mInfos.Count; i++)
			{
				ParameterInfo[] pi = mInfos[i].GetParameters();
				bool remove = false;
				for (int e = 0; e < mInfos.Count; e++)
				{
					if (i == e)
						continue;

					ParameterInfo[] pe = mInfos[e].GetParameters();
					if (pe[0].ParameterType.IsSubclassOf(pi[0].ParameterType) == true &&
						pe[1].ParameterType.GetElementType().IsSubclassOf(pi[1].ParameterType.GetElementType()) == true)
					{
						remove = true;
					}
				}
				if (remove == true)
					sel.Add(mInfos[i]);
			}

			foreach (MethodInfo mi in mInfos)
			{
				if (sel.Contains(mi) == false)
					return mi;
			}
			return null;
		}

		class Converter
		{
			public static bool Standard(Coloring Source, ref Coloring Destination)
			{
				if (Destination.Data != null && Destination.Parameter != null)
				{
					if (Source.Data.GetType() == Destination.Data.GetType() && Source.Parameter.GetType() == Destination.Parameter.GetType())
					{
						Destination.Initialize(Source.FractalParameter, Source.Parameter, Source.Data);
						return true;
					}
				}
				if (Source.GetType().BaseType != Destination.GetType().BaseType)
					return false;
				Destination.Initialize(Source.FractalParameter, Source.Parameter, Source.Data);
				return true;
			}

			public static bool ComplexFractalColoringColoring_ComplexFractalColoringIterationBasedColoring
				(ComplexFractalColoring.Coloring Source, ref ComplexFractalColoring.IterationBased.Coloring Destination)
			{
				ComplexFractalParameter fPara = (ComplexFractalParameter)Source.FractalParameter;
				ComplexFractalColoring.ColoringParameter cPara = (ComplexFractalColoring.ColoringParameter)Source.Parameter;
				ComplexFractalColoring.ColoringData data = (ComplexFractalColoring.ColoringData)Source.Data;
				Destination.Initialize(fPara, cPara, data);
				return true;
			}

			public static bool ComplexFractalColoringColoring_ComplexFractalColoringEscapeTimeHSVColoring
				(ComplexFractalColoring.Coloring Source, ref ComplexFractalColoring.EscapeTime.HSVColoring Destination)
			{
				ComplexFractalParameter fPara = (ComplexFractalParameter)Source.FractalParameter;
				ComplexFractalColoring.ColoringParameter cPara = ComplexFractalColoring.PeriodicColoringParameter.Default;
				ComplexFractalColoring.ColoringData sourceData = (ComplexFractalColoring.ColoringData)Source.Data; ;
				ComplexFractalColoring.PeriodicColoringData data = new ComplexFractalColoring.PeriodicColoringData(fPara.PixelWidth, fPara.PixelHeight);
				data.iterations = sourceData.iterations;
				data.c = sourceData.c;
				data.z = sourceData.z;
				Destination.Initialize(fPara, cPara, data);
				Destination.PostCalculation();
				return true;
			}

			public static bool ComplexFractalColoringRouteLengthBasedColoring_ComplexFractalColoringRouteLengthBasedUserColoring
				(ComplexFractalColoring.RouteLengthBased.Coloring Source, ref ComplexFractalColoring.RouteLengthBased.UserColoring Destination)
			{
				ComplexFractalColoring.RouteLengthBased.UserColoringParameter para = ComplexFractalColoring.RouteLengthBased.UserColoringParameter.Default;
				ComplexFractalColoring.PeriodicColoringParameter sPara = (ComplexFractalColoring.PeriodicColoringParameter)Source.Parameter;
				para.AutoSetFrequency = sPara.AutoSetFrequency;
				para.Frequency = sPara.Frequency;
				para.NumberOfColorPeriods = sPara.NumberOfColorPeriods;
				para.Translation = sPara.Translation;
				Destination.Initialize(Source.FractalParameter, para, Source.Data);
				return true;
			}
		}
	}
}
